package storagememory

import (
	"math"
	"sync"

	"gitee.com/lyuanbo/stock-plus/pkg/stock/types"
	"github.com/gammazero/deque"
)

type RingBuffer struct {
	dq      deque.Deque[*types.Kline]
	maxSize int
	Mux     sync.RWMutex
}

func NewKlineCache(size int) *RingBuffer {
	return &RingBuffer{
		dq:      deque.Deque[*types.Kline]{},
		maxSize: size,
		Mux:     sync.RWMutex{},
	}
}

func (k *RingBuffer) PushBack(kline *types.Kline) {
	k.dq.PushBack(kline)

	if k.dq.Len() > k.maxSize {
		k.dq.PopFront()
	}
}

func (k *RingBuffer) SafePushBack(kline *types.Kline) {
	k.Mux.Lock()
	defer k.Mux.Unlock()

	k.dq.PushBack(kline)

	if k.dq.Len() > k.maxSize {
		k.dq.PopFront()
	}
}

func (k *RingBuffer) GetLast(n int) []*types.Kline {
	length := k.dq.Len()
	if n > length {
		n = length
	}

	result := make([]*types.Kline, 0, int(math.Abs(float64(n))))
	if n >= 0 {
		for i := 0; i < n; i++ {
			result = append(result, k.dq.At(length-n+i))
		}
		return result
	}

	n = length + n
	if n < 0 {
		n = 0
	}
	for i := n; i < length; i++ {
		v := k.dq.At(i)
		if v != nil {
			result = append(result, v)
		}
	}
	return result
}

func (k *RingBuffer) SafeGetLast(n int) []*types.Kline {
	k.Mux.RLock()
	defer k.Mux.RUnlock()

	length := k.dq.Len()
	if n > length {
		n = length
	}

	result := make([]*types.Kline, 0, int(math.Abs(float64(n))))
	if n >= 0 {
		for i := 0; i < n; i++ {
			result = append(result, k.dq.At(length-n+i))
		}
		return result
	}

	n = length + n
	if n > 0 {
		for i := n; i < length; i++ {
			v := k.dq.At(i)
			if v != nil {
				result = append(result, v)
			}
		}
	}
	return result
}

func (k *RingBuffer) GetIndex(n int) *types.Kline {
	length := k.dq.Len()
	if length == 0 {
		return nil
	}

	if n > length {
		n = length
	}

	if n < 0 {
		n = length + n
	}

	return k.dq.At(n)
}

func (k *RingBuffer) SafeGetIndex(n int) *types.Kline {
	k.Mux.RLock()
	defer k.Mux.RUnlock()

	length := k.dq.Len()
	if length == 0 {
		return nil
	}

	if n > length {
		n = length
	}

	if n < 0 {
		n = length + n
	}

	return k.dq.At(n)
}

func (k *RingBuffer) GetLatest() (*types.Kline, error) {
	if k.dq.Len() == 0 {
		return nil, ErrCacheEmpty
	}

	return k.dq.Back(), nil
}

func (k *RingBuffer) SafeGetAll() []*types.Kline {
	k.Mux.RLock()
	defer k.Mux.RUnlock()

	length := k.dq.Len()
	result := make([]*types.Kline, 0, length)
	for i := 0; i < length; i++ {
		result = append(result, k.dq.At(i))
	}
	return result
}

func (k *RingBuffer) SafeGetLatest() *types.Kline {
	k.Mux.RLock()
	defer k.Mux.RUnlock()

	if k.dq.Len() == 0 {
		return nil
	}

	return k.dq.Back()
}

func (k *RingBuffer) Len() int {
	return k.dq.Len()
}

func (k *RingBuffer) Clear() {
	k.dq.Clear()
}

func (k *RingBuffer) SafeLen() int {
	k.Mux.RLock()
	defer k.Mux.RUnlock()
	return k.dq.Len()
}

func (k *RingBuffer) SafeClear() {
	k.Mux.Lock()
	defer k.Mux.Unlock()
	k.dq.Clear()
}
